/*
 * Unicast.cpp
 *
 *  Created on: 08/ott/2013
 *      Author: alessandrob
 */
#include<iostream>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<opencv2/opencv.hpp>
#include<opencv2/features2d/features2d.hpp>
#include<opencv2/highgui/highgui.hpp>
#include "Utility.h"
#define PORT 5002

void unicast(int clientsNumber,int overlap,int loads,int threshold)
{
	int i,size,sockfd,countClientsConnected=0,y=0,row,k,dataSent,cols,rows,dataReceived;
	struct sockaddr_in server_address, client_address;
	vector<sockaddr_in> clients;
	struct list_nodes nodeList[clientsNumber+1];
	unsigned int slen=sizeof(client_address);
	float bandwidth;
	char* ipAddress;
	char infoPacket[9];
	char ack;
	char sync,command[35];
	double sumProc=0,det_time;
	Mat splitImage[clientsNumber+1];
	Mat original,image;
	vector<int> params = vector<int>(2);
	params[0]=100;
	vector<uchar> data;
	cout<<"UNICAST MODE. Setting up BRISK."<<endl;
	vector<KeyPoint> distributedKpa,distributedKpb;

	Ptr<FeatureDetector> detector;
	detector = new BRISK(threshold,4);
	Ptr<DescriptorExtractor> descriptorExtractor;
	descriptorExtractor = new BRISK(threshold,4);

	Mat descriptors,distributedDescriptors;
	vector<KeyPoint> kp,distributedKp;
	FILE* file = fopen("/home/ubuntu/test.txt","a+");
	if(file == NULL)
		return;

	cout<<"Setting up TCP server"<<endl;
	//Setting camera server cpu speed
	nodeList[0].CPUspeed = 5000000;
	//TCP Socket creation
	if ((sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1)
		printErrorLog("Server socket creation error. ");
	cout<<"Server TCP socket created correctly."<<endl;
	memset((char *) &server_address, 0, sizeof(server_address));
	server_address.sin_family = AF_INET;
	server_address.sin_port = htons(PORT);
	server_address.sin_addr.s_addr = htonl(INADDR_ANY);

	//Binding on port: 5002
	if (bind(sockfd, (struct sockaddr *)&server_address, sizeof(server_address))==-1)
		printErrorLog("Wrong binding. The server is NOT running");
	cout<<"Correct binding. The server is working correctly, listening for connections on port: 5002."<< endl;

	//Waiting for connections. All the clients have to connect to the server, before starting the
	//network test performance.
	listen(sockfd,clientsNumber);
	while(countClientsConnected!=clientsNumber)
	{
		if((nodeList[countClientsConnected+1].socket = accept(sockfd,(struct sockaddr*)&client_address,&slen))==-1)
			printErrorLog("Invalid client socket");
		if(recv(nodeList[countClientsConnected+1].socket, &nodeList[countClientsConnected+1].CPUspeed, sizeof(int), 0)<0)
			printErrorLog("CPU speed not received correctly");
		printf("Beaglebone connected: %s:%d\n",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
		clients.push_back(client_address);
		countClientsConnected++;
	}
	//For all the clients, the server has to test the real bandwidth
	for(i=0;i<clientsNumber;i++)
	{
		ipAddress = inet_ntoa(clients[i].sin_addr);
		strcpy(command,"iperf -c");
		strcat(command,inet_ntoa(clients[i].sin_addr));
		strcat(command," -t 5 >/home/ubuntu/iperfLog.txt");
		cout<<"Starting bandwidth test: "<<inet_ntoa(clients[i].sin_addr)<<endl;
		//system(command);
		//Iperf log file parsing to get the real bandwidth
		bandwidth=getBandwidth(false);
		if(bandwidth==0)
			printErrorLog("Network performance report file doesn't exist");
		nodeList[i+1].ipAddress=ipAddress;
		nodeList[i+1].bandwidth=bandwidth*1000000;
		printf("Bandwidth: %fMbps\n",nodeList[i+1].bandwidth/1000000);
	}
	//nodes ordered by decreasing bandwidth value
	orderNodes(clientsNumber,nodeList);
	//Computing loads
	computeLoadUnicast(clientsNumber,nodeList,overlap,loads);

	original = imread("/home/ubuntu/monte.jpg",0);
	//original = imread("/home/ubuntu/scacchiera.jpg",0);
	//original = imread("/home/ubuntu/skyscraper-wallpaper-640x480.jpg",0);
	//original = imread("/home/ubuntu/201263191229.jpg",0);
	//original = imread("/home/ubuntu/manhattan.jpg",0);
	//original = imread("/home/ubuntu/27_Vga.jpg",0);
	//original = imread("/home/ubuntu/511-100x100.jpg",0);
	//original = imread("/home/ubuntu/60x60.jpg",0);
	//original = imread("/home/ubuntu/landscape1.jpg",0);
	cout<<"BRISK: CAMERA ONLY"<<endl;
	cout<<"Timer started"<<endl;
	det_time = (double)getTickCount();
	//Computing BRISK
	double processing_time;
	Mat serverKps;
	//for(i=0;i<10;i++)
	//{
	det_time=(double)getTickCount();
	detector->detect(original,kp);
	//drawKeypoints(original,kp,serverKps);
	//imwrite("/home/ubuntu/serverKP.jpg",serverKps,params);
	descriptorExtractor->compute(original,kp,descriptors);
	processing_time = ((double)getTickCount() - det_time)/getTickFrequency();
	cout<<"Processing time: "<<processing_time*1000<<"ms"<<endl;
	//sumProc+=processing_time;
		//if(i==9)
		//	fprintf(file,"%f\n",processingTime);
		//else
		//	fprintf(file,"%f ",processingTime);
	//}
	//cout<<"Processing total time: "<<(sumProc/10)*1000<<"ms"<<endl;
	cout<<"Keypoints detected: "<<kp.size()<<endl<<endl;
	if(clientsNumber>0)
	{
		//Distributed BRISK
		cout<<"DISTRIBUTED BRISK"<<endl;
		cout<<"Timer started"<<endl;
		det_time=(double)getTickCount();
		//Client synchronization
		int s;
		sync = 's';
		for(k=0;k<clientsNumber;k++)
		{
			s = nodeList[k+1].socket;
			if(send(s,&sync,sizeof(sync),0)==-1)
				printErrorLog("Synchronization failed");
		}
		double sync_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Synchronization time: "<<sync_time*1000<<"ms"<<endl;
		overlap = (int)(original.rows*overlap)/100;
		//Splitting by row
		for(i=0;i<clientsNumber+1;i++)
		{
			row = floor((original.rows*nodeList[i].load)/100);
			cout<<"row: "<<row<<endl;
			nodeList[i].width = original.cols;
			cout<<"From "<<y<<" to "<<y+row<<endl;
			if(i==clientsNumber)
			{
				if((y+row)!=original.rows)
				{
					row=original.rows-y;
					cout<<"row: "<<row<<endl;
				}
				splitImage[i] = Mat(original,Rect_<int>(0,y,original.cols,row));
			}
			else
			{
				splitImage[i] = Mat(original,Rect_<int>(0,y,original.cols,row+overlap));
			}
			nodeList[i].height = row;
			y=y+row;
		}
		//Disegno le due parti senza keypoints
		imwrite("/home/ubuntu/image0.jpg",splitImage[0],params);
		imwrite("/home/ubuntu/image1.jpg",splitImage[1],params);

		double splittingtime = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<"Splitting time: T+"<<splittingtime*1000<<"ms"<<endl;
		double sendtime;
		//For each client, send the image
		for(i=0;i<clientsNumber;i++)
		{
			cols = splitImage[i+1].cols;
			rows = splitImage[i+1].rows;
			infoPacket[0] = (cols>>24);
			infoPacket[1] = (cols>>16);
			infoPacket[2] = (cols>>8);
			infoPacket[3] = (cols);
			infoPacket[4] = (rows>>24);
			infoPacket[5] = (rows>>16);
			infoPacket[6] = (rows>>8);
			infoPacket[7] = (rows);
			if(i==(clientsNumber-1))
				//last client
				infoPacket[8] = 1;
			else
				infoPacket[8] = 0;
			s = nodeList[i+1].socket;
			if(send(s,infoPacket,sizeof(char)*9,0)!=sizeof(char)*9)
				printErrorLog("Info packet not sent correctly");
			uchar support[cols*rows];
			memcpy(&support,splitImage[i+1].data,cols*rows);
			double starttime = ((double)getTickCount() - det_time)/getTickFrequency();
			cout<<"Start sending time (client "<<inet_ntoa(clients[i].sin_addr)<<"): T+"<<starttime*1000<<"ms"<<endl;
			dataSent = send(s,support,cols*rows,0);
			sendtime =((double)getTickCount() - det_time)/getTickFrequency();
			cout<<"Sending time (client "<<inet_ntoa(clients[i].sin_addr)<<"): T+"<<sendtime*1000<<"ms"<<endl;
			//cout<<"Number of bytes actually sent: "<<dataSent<<endl;
			if(dataSent!=(cols*rows))
				printErrorLog("Data not sent correctly");
		}
		//Waiting for the ACK sent by the last client
		s = nodeList[clientsNumber].socket;
		if(recv(s, &ack, sizeof(char), 0)<0)
			printErrorLog("ACK not received correctly");
		cout<<"ACK received. Ready to compute BRISK"<<endl;
		if(ack=='f')
		{
			double ack_time = ((double)getTickCount() - det_time)/getTickFrequency();
			cout<<"ACK time: T+"<<ack_time*1000<<"ms"<<endl;
			//Processing server part
			//for(i=0;i<10;i++)
			//{
			//PRIMA IMMAGINE
			det_time = (double)getTickCount();
			//detector->detect(splitImage[0],distributedKp);
			detector->detect(splitImage[0],distributedKpa);
			Mat distributedKPs,distributedDescriptorsA,distributedDescriptorsB;
			drawKeypoints(splitImage[0],distributedKpa,distributedKPs);
			imwrite("/home/ubuntu/image0KP.jpg",distributedKPs,params);
			descriptorExtractor->compute(splitImage[0],distributedKpa,distributedDescriptorsA);
			det_time = ((double)getTickCount() - det_time)/getTickFrequency();
			//if(i!=0)
			//7	sum=sum+det_time;
			size = distributedKpa.size();
			cout<<"Slice 0: "<<size<<endl;

			//detector->detect(splitImage[0],distributedKp);
			/*det_time = (double)getTickCount();
			//detector->detect(splitImage[0],distributedKp);
			detector->detect(splitImage[1],distributedKpb);
			drawKeypoints(splitImage[1],distributedKpb,distributedKPs);
			imwrite("/home/ubuntu/image1KP.jpg",distributedKPs,params);
			descriptorExtractor->compute(splitImage[1],distributedKpb,distributedDescriptorsB);
			det_time = ((double)getTickCount() - det_time)/getTickFrequency();
			//if(i!=0)
			//7	sum=sum+det_time;
			size = distributedKpb.size();
			cout<<"Slice 1: "<<size<<endl;*/
			//}
			//double processingtime = (sum/9)*1000;
			//cout<<"Server processing time: "<<processingtime<<"ms"<<endl;
			//nodeList[0].time=(ack_time+(sum/9))*1000;
			//cout<<"Server TOTAL TIME: T+"<<nodeList[0].time<<"ms"<<endl;

			//Timer stopped
			//cout<<"Keypoints found: "<<size<<endl;
			nodeList[0].keypointsDetected = distributedKpa.size();
			for(i=0;i<nodeList[0].keypointsDetected;i++)
			{
				nodeList[0].keypoints.push_back(distributedKpa[i].pt.x);
				nodeList[0].keypoints.push_back(distributedKpa[i].pt.y);
			}
			//copying distributed descriptors
			nodeList[0].descriptorsSize = nodeList[0].keypointsDetected*64;
			for(i=0;i<nodeList[0].descriptorsSize;i++)
				nodeList[0].descriptors.push_back(distributedDescriptorsA.data[i]);
			/*nodeList[1].descriptorsSize = nodeList[1].keypointsDetected*64;
			for(i=0;i<nodeList[1].descriptorsSize;i++)
					nodeList[1].descriptors.push_back(distributedDescriptorsB.data[i]);*/
			//Waiting for the clients processing final times
			cout<<"RECEIVING DATA FROM THE CLIENTS"<<endl<<endl;
			//int keypointsDetected;
			receiveData(clientsNumber,nodeList);
			int result = check(clientsNumber,nodeList,kp,descriptors);
			if(result==0)
				cout<<"Check correct"<<endl;
			else if(result==1)
				printErrorLog("Keypoints number not corrected");
			else if(result==2)
				printErrorLog("At least one keypoint different");
		}
	}
	close(sockfd);
	fclose(file);
}


